﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//引入Socket命名空间
using System.Net;
using System.Net.Sockets;
using SerialPortHelper;
using System.Threading;
namespace SocketHelper
{
    public class ClientHelper
    {
        private Socket client = null ;
        private readonly ManualResetEvent TimeoutObject = new ManualResetEvent(false);  
        //IP地址
        public IPAddress IP { get; set; }
        //端口号
        public IPEndPoint Point { get; set; }
        public string ServerIP { get; set; }
        public int Port { get; set; }
        public bool Connected { get { return this.client.Connected; } }
        public ClientHelper()
        {
            client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);           
        }
        /// <summary>
        /// 客户端创建连接
        /// </summary>
        public void Connect()
        {
            this.IP = IPAddress.Parse(this.ServerIP);
            this.Point = new IPEndPoint(IP, this.Port);
            TimeoutObject.Reset(); 
            //连接服务器
            //this.client.Connect(this.Point);
            /*TCP客户端可以向TcpServer发送数据包时，若网络断开，原来的连接就会失效，出现以下错误提示:
            断开套接字连接后，只能通过异步方式再次重新连接，而且只能连接到不同的 EndPoint。在操作完成前，在将不会退出的线程上必须调用 BeginConnect
            导致无法发送数据包，如何处理呢？主要原因还是已断开的连接无法再使用，只能重新创建，因为我们可以采用自动重新创建连接的方式来解决*/
            if (client == null)
            {
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //client.Connect(this.Point); //配置服务器IP与端口
                client.BeginConnect(this.Point, CallBackMethod, client);
            }
            //--断开自动重新创建连接
            if (!client.Connected)
            {
                client.Close();
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //client.Connect(this.Point); //配置服务器IP与端口
                client.BeginConnect(this.Point, CallBackMethod, client);
             }
            //阻塞当前线程             
            if (TimeoutObject.WaitOne(2000, false))
            {
                //MessageBox.Show("网络正常");  
            }
            else
            {
                client.Close();
                throw new Exception("连接超时");
            }  
        }
        /*您可能注意到了，.Net的System.Net.Sockets.TcpClient和System.Net.Sockets.Socket都没有直接为Connect/BeginConnect提供超
        时控制机制。因此，当服务器未处于监听状态，或者发生网络故障时，客户端连接请求会被迫等待很长一段时间，直到抛出异常。默
        认的等待时间长达20~30s。.Net Socket库的SocketOptionName.SendTimeout提供了控制发送数据的超时时间，但并非本文讨论的连
        接请求的超时时间。*/
        //--异步回调方法         
        private void CallBackMethod(IAsyncResult asyncresult)  
        {  
            //使阻塞的线程继续          
            TimeoutObject.Set();  
        }
        public byte[] ClientReceiveMsg()
        {

            try
            {
                byte[] buffer = new byte[14];
                 //将接收过来的数据放到buffer中，并返回实际接受数据的长度
                 int n = client.Receive(buffer);
                 return buffer;
            }
            catch (Exception ex)
            {               
                throw new Exception(ex.Message);
            }
        }
        public void ClientSendData(string data)
        {
            try
            {
                if (this.client != null)
                {
                    byte[] buffer = new SerialPortHelper.AlgorithmHelper().From16ToBtyes(data);
                    this.client.Send(buffer);
                }
                else throw new Exception("客户端与服务器连接异常");
            }
            catch (Exception ex)
            {

                throw new Exception("发送失败，客户端与服务器连接异常；错误信息：" + ex.Message);
            }           
        }
        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close()
        {
            if(this.client !=null && this.client.Connected)
            {
                //关闭Socket之前，首选需要把双方的Socket Shutdown掉
                this.client.Shutdown(SocketShutdown.Both);
                //Shutdown掉Socket后主线程停止10ms，保证Socket的Shutdown完
                System.Threading.Thread.Sleep(10);
                //关闭客户端Socket,清理资源
                this.client.Close();
                //断开后还要重新new一个新的socket，否则再一次连接时出现“无法访问已释放对象”
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);     
            }
            
        }
    }
}
